#include "do_dos_cmd.h"
#include "cmd.h"
#include "ff.h"

static char current_path[_MAX_LFN] = "/";

static void set_label(char *label);
static int fat_unlink(const char *fpath);

int do_format(int argc, char **argv)
{
	FRESULT ret;

	if (argc == 2) {
		char *logic_number = argv[1];
		ret = f_mkfs(logic_number, 0, 0);

		if (ret == FR_OK) {
			set_label("New Volume");
		}

	} else {
		man_format();
	}

	return 0;
}

void man_format(void)
{
	printf("Usage:\n\tformat [logic drive number]\n"
		"\tEx: format 0:");
}

static void show_current_path(void)
{
	f_getcwd(current_path, sizeof current_path);
	printf("%s\n", current_path);
}

int do_cd(int argc, char **argv)
{
	FRESULT ret = FR_OK;

	if (argc >= 2) {
		char *path = argv[1];
		ret = f_chdir(path);

		switch (ret) {
		case FR_OK:
			show_current_path();
			break;
		case FR_NO_PATH:
			fprintf(stderr, "Dicrectory not found.\n");
			break;
		case FR_DENIED:
			fprintf(stderr, "No permission.\n");
			break;
		default:
			fprintf(stderr, "Unknown error.\n");
			break;
		}
	} else {
		show_current_path();
		ret = FR_OK;
	}

	return (int)ret;
}

void man_cd(void)
{
	printf("Usage:\n\tcd [directory]\n");
}


/* path maybe modified
 *  * we can use 'char path[]' or tell user if a parameter without const, it may be modified later */
FRESULT scan_files (
    char* path        /* Start node to be scanned (also used as work area) */
)
{
    FRESULT res;
    FILINFO fno;
    DIR dir;
    int i;
    char *fn;   /* This function assumes non-Unicode configuration */
#if _USE_LFN
    static char lfn[_MAX_LFN + 1];   /* Buffer to store the LFN */
    fno.lfname = lfn;
    fno.lfsize = sizeof lfn;
#endif

    res = f_opendir(&dir, path);                       /* Open the directory */
    if (res == FR_OK) {
        i = strlen(path);
        for (;;) {
            res = f_readdir(&dir, &fno);                   /* Read a directory item */
            if (res != FR_OK || fno.fname[0] == 0) break;  /* Break on error or end of dir */
            if (fno.fname[0] == '.') continue;             /* Ignore dot entry */
#if _USE_LFN
            fn = *fno.lfname ? fno.lfname : fno.fname;
#else
            fn = fno.fname;
#endif
            //if (fno.fattrib & AM_DIR) {                    /* It is a directory */
            //    sprintf(&path[i], "\\%s", fn);
            //    res = scan_files(path);
            //    path[i] = 0;
            //    if (res != FR_OK) break;
            //} else {                                       /* It is a file. */
			//    printf("%s\n", fn);
            //}
			printf("%s\n", fn);
        }
        f_closedir(&dir);
    }

    return res;
}

int dir(char *path)
{
	int ret;
	char full_path[256] = {0};

	FRESULT fr;
	FILINFO fno;

#if _USE_LFN
	static char lfn[_MAX_LFN + 1];   /* Buffer to store the LFN */
	fno.lfname = lfn;
	fno.lfsize = sizeof lfn;
#endif
	sprintf(full_path, "0:%s", path);
	fr = f_stat(full_path, &fno);

	switch (fr) {
		case FR_OK:
			printf("%u/%02u/%02u\t%02u:%02u"
				"\t%u %s\n",
				(fno.fdate >> 9) + 1980, fno.fdate >> 5 & 15, fno.fdate & 31,
				fno.ftime >> 11, fno.ftime >> 5 & 63,
				fno.fsize, full_path);
			ret = 0;
			break;
		case FR_NO_FILE:
			fprintf(stderr, "%s is not exist.\n", path);
			ret = -1;
			break;
		default:
			printf("An error occured: %d\n", fr);
			ret = -1;
			break;
	}
	return 0;
}

int do_dir(int argc, char **argv)
{
	static char root_path[256] = "0:"; /* I think 256 is long enough to support LFN */
	if (argc == 1) {
		scan_files(current_path);
	}
	else if (argc == 2) {
		char* path = argv[1];
		dir(path);
	}
	return 0;
}

void man_dir(void)
{
	printf("Usage:\n\tdir [file/directory]\n");
}

int do_mkdir(int argc, char **argv)
{
	FRESULT ret = FR_OK;

	if (argc == 2) {
		char *path = argv[1];
		ret = f_mkdir(path);
		switch(ret) {
		case FR_OK:
			break;
		case FR_EXIST:
			fprintf(stderr, "%s exist.\n", path);
			break;
		default:
			fprintf(stderr, "Unkown error %d\n", (int)ret);
			break;
		}
	} else {
		man_mkdir();
	}

	return ret;
}

void man_mkdir(void)
{
	printf("Usage:\n\tmkdir [directory]\n");
}

int do_rmdir(int argc, char **argv)
{
	if (argc == 2) {
		char *path = argv[1];
		return fat_unlink(path);
	} else {
		man_rmdir();
	}
	return 0;
}

void man_rmdir(void)
{
	printf("Usage:\n\trmdir [directory]\n");
}

/*
 * get label of default drive
 * */
static char *get_label(void)
{
	static char disk_label[32];
	memset(disk_label, 0x0, sizeof disk_label);
	f_getlabel("0:", disk_label, 0);
	return disk_label;	
}

/*
 * set label of default drive
 * */
static void set_label(char *label)
{
	if (label)
		f_setlabel(label);
}

int do_label(int argc, char **argv)
{
	if (argc == 1)
		printf("Voluem in drive %s is %s\n", "0:", get_label());
	else if(argc == 2) {
		char *label = argv[1];
		set_label(label);
	} else {
		man_label();		
	}
	return 0;
}

void man_label(void)
{
	printf("Usage\n\tlabel [no argument] - Show label\n\tlabel [label name] - Set label\n");
}


static int type(char *fpath)
{
	FIL fp;
	FRESULT ret;
	UINT bytes_read = 0;
	char buf[1024] = {0};

	char full_path[256] = {0};

	sprintf(full_path, "0:%s", fpath);

	if ((ret = f_open(&fp, full_path, FA_READ)) != FR_OK) {
		fprintf(stderr, "open %s fails, error %d\n", full_path, (int)ret);
		return -1;
	}

	/* I write feof() here, this makes a hard to find bug! */
	while (f_eof(&fp) == 0) {
		UINT i;
		if (f_read(&fp, buf, 1024, &bytes_read) != FR_OK) {
			printf("f_read error!\n");
			break;
		}

		if (!bytes_read) {
			break;
		}
		for (i = 0; i < bytes_read; ++i)
			printf("%c", buf[i]);
	}
	printf("\n");

	f_close(&fp);
	return 0;
}

int do_type(int argc, char **argv)
{
	if (argc == 2) {
		char *fpath = argv[1];
		return type(fpath);
	} else {
		man_type();
	}

	return 0;
}

void man_type(void)
{
	printf("Usage:\n\ttype [file]\n");
}

static int copy(const char *f_src, const char *f_dst)
{
	FIL fp_src;
	FIL fp_dst;

	FRESULT ret;
	UINT bytes_read = 0;
	UINT bytes_write = 0;
	char buf[1024] = {0};

	char full_dst_path[256] = {0};

	sprintf(full_dst_path, "0:%s", f_dst);

	if ((ret = f_open(&fp_dst, full_dst_path, FA_OPEN_ALWAYS|FA_WRITE)) != FR_OK) {
		fprintf(stderr, "open %s fails, error %d\n", full_dst_path, (int)ret);
		return -1;
	}

	if ((ret = f_open(&fp_src, f_src, FA_READ)) != FR_OK) {
		fprintf(stderr, "open %s fails, error %d\n", f_src, (int)ret);
		return -1;
	}

	/* I write feof() here, this makes a hard to find bug! */
	while (f_eof(&fp_src) == 0) {
		UINT i;
		if (f_read(&fp_src, buf, 1024, &bytes_read) != FR_OK) {
			printf("f_read error!\n");
			break;
		}
		f_write(&fp_dst, buf, bytes_read, &bytes_write);
	}

	f_close(&fp_src);
	f_close(&fp_dst);

	return 0;
}

int do_copy(int argc, char **argv)
{
	if (argc == 3) {
		char *f_src = argv[1];
		char *f_dst = argv[2];
		return copy(f_src, f_dst);
	} else {
		man_copy();
	}

	return 0;
}

void man_copy(void)
{
	printf("Usage:\n\tcopy [src file] [dst file]\n");
}

static int fat_unlink(const char *path)
{
	return (int)f_unlink(path);
}

int do_del(int argc, char **argv)
{
	if (argc == 2) {
		char *path = argv[1];
		return fat_unlink(path);
	} else {
		man_del();
	}

	return 0;
}

void man_del(void)
{
	printf("Usage:\n\tdel [file]\n");
}
